/*
 * net/balusc/webapp/FileServlet.java
 *
 * Copyright (C) 2009 BalusC
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this library.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package org.rcs.servlets;

import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.rcs.client.ClientFactory;
import org.rcs.client.ClientRender;
import org.rcs.folders.AudioFolder;
import org.rcs.folders.BasicItemsSystem;
import org.rcs.folders.CompositeFolder;
import org.rcs.folders.ItemsFactory;
import org.rcs.folders.PictureFolder;
import org.rcs.folders.VideoFolder;
import org.rcs.folders.VirtualItem;
import org.rcs.folders.VirtualItemsSytem;

import org.rcs.servlets.utils.CustomHttpServletRequest;
import org.rcs.servlets.utils.XMLConfig;

/**
 * A file servlet supporting resume of downloads and client-side caching and
 * GZIP of text content. This servlet can also be used for images, client-side
 * caching would become more efficient. This servlet can also be used for text
 * files, GZIP would decrease network bandwidth.
 *
 * @author BalusC
 * @author Ruben
 * @link http://balusc.blogspot.com/2009/02/fileservlet-supporting-resume-and.html
 */
public class FileServlet extends HttpServlet {
  // Properties ---------------------------------------------------------------------------------

  private VirtualItemsSytem itemsSystem;
  private static FileServletConfig config = null;
  private ItemsFactory[] factories;

  private FileServletConfig getConfig() {
    config = XMLConfig.get(config, FileServletConfig.class);

    return config;
  }

  // Actions ------------------------------------------------------------------------------------
  /**
   * Initialize the servlet.
   *
   * @throws javax.servlet.ServletException
   * @see HttpServlet#init().
   */
  @Override
  public void init() throws ServletException {

    factories = createFactories();

    for (ItemsFactory factory : factories) {
      factory.init();
    }

    itemsSystem = new BasicItemsSystem(new CompositeFolder("",
            new VideoFolder(factories),
            new AudioFolder(factories),
            new PictureFolder(factories)));
  }

  @Override
  public void destroy() {
    for (ItemsFactory factory : factories) {
      factory.destroy();
    }

  }

  private ItemsFactory[] createFactories() {
    ServiceLoader<ItemsFactory> loader = ServiceLoader.load(ItemsFactory.class) ;
    List<ItemsFactory> result = new ArrayList<ItemsFactory>();
    
    for (ItemsFactory factory : loader)
      result.add(factory) ;
    
//    List<ItemsFactory> result = new ArrayList<ItemsFactory>();
//    String[] factories = getConfig().getFactories();
//
//    for (String factory : factories) {
//      try {
//        Class<? extends ItemsFactory> clazz = Class.forName(factory).asSubclass(ItemsFactory.class);
//        ItemsFactory itemFactory = clazz.newInstance();
//        result.add(itemFactory);
//      } catch (InstantiationException ex) {
//        Logger.getLogger(FileServlet.class.getName()).log(Level.SEVERE, null, ex);
//      } catch (IllegalAccessException ex) {
//        Logger.getLogger(FileServlet.class.getName()).log(Level.SEVERE, null, ex);
//      } catch (ClassNotFoundException ex) {
//        Logger.getLogger(FileServlet.class.getName()).log(Level.SEVERE, null, ex);
//      }
//    }
//
    return result.toArray(new ItemsFactory[result.size()]);
  }

  /**
   * Process HEAD request. This returns the same headers as GET request, but
   * without content.
   *
   * @param request
   * @param response
   * @throws javax.servlet.ServletException
   * @throws java.io.IOException
   * @see HttpServlet#doHead(HttpServletRequest, HttpServletResponse).
   */
  @Override
  protected void doHead(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    // Process request without content.
    processRequest(request, response, false);
  }

  /**
   * Process GET request.
   *
   * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse).
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    // Process request with content.
    processRequest(request, response, true);
  }

  private String getRequestPath(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String requestedPath = request.getPathInfo();

    // Check if file is actually supplied to the request URL.
    if (null != requestedPath) {
      requestedPath = URLDecoder.decode(requestedPath, "UTF-8");
    }

    return requestedPath;
  }

  /**
   * Process the actual request.
   *
   * @param request The request to be processed.
   * @param response The response to be created.
   * @param content Whether the request body should be written (GET) or not
   * (HEAD).
   * @throws IOException If something fails at I/O level.
   */
  private void processRequest(HttpServletRequest request, HttpServletResponse response, boolean content) throws IOException {
    request = new CustomHttpServletRequest(request);

    try {
      VirtualItem item = null;
      String requestedPath = getRequestPath(request, response);

      if (null != requestedPath) {
        item = itemsSystem.getVirtualItem(requestedPath);
      }

      item = filterItem(request, item);

      if (null != item) {
        item.sendContent(response, request, requestedPath, !content);
      } else {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
      }
    } catch (IOException ex) {
      throw ex;
    } catch (Exception ex) {
      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getLocalizedMessage());
    }
  }

  private VirtualItem filterItem(HttpServletRequest request, VirtualItem item) {
    if (null != item) {
      ClientFactory factory = ClientFactory.get();
      ClientRender render = factory.createClientRender(request);

      if (null != render) {
        item = render.filter(item);
      }
    }

    return item;
  }
}
